package com.yc154.filter;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.yc154.util.JwtUtils;
import io.jsonwebtoken.Claims;
import lombok.Data;
import org.reactivestreams.Publisher;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.core.io.buffer.DataBuffer;
import org.springframework.core.io.buffer.DataBufferUtils;
import org.springframework.http.MediaType;
import org.springframework.http.server.RequestPath;
import org.springframework.http.server.reactive.ServerHttpRequestDecorator;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.http.server.reactive.ServerHttpResponseDecorator;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.*;

@Component
public class UserAuthFilter
        extends AbstractGatewayFilterFactory<UserAuthFilter.Config> {

    UserAuthFilter() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(UserAuthFilter.Config config) {
        return ((exchange, chain) -> {
            RequestPath path = exchange.getRequest().getPath();
            if (config.enabled == false) {
                // 未开启, 直接放行
                return chain.filter(exchange);
            } else if ("/auth/login".equals(path.toString())) {
                // 登录请求, 直接放行
                return login(exchange, chain);
            } else if ("/auth/myinfo".equals(path.toString())||
                    "/user/auth/myinfo".equals(path.toString())||
                    "/cloud-user/auth/myinfo".equals(path.toString())) {
                // 登录请求, 直接放行
                return myinfo(exchange, chain);
            } else if ("/auth/logout".equals(path.toString())) {
                // 登录请求, 直接放行
                return logout(exchange, chain);
            } else {
                // 登录请求, 直接放行
                return chain.filter(exchange);
            }
        });

    }

    @Override
    public String name() {
        return "UserAuthFilter";
    }

    private Mono<Void> login(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("------------------------------ login");
        ServerHttpResponse response = exchange.getResponse();
        // 实现后置拦截
        // 读取响应体, 响应体读取完成之后, 需要重写响应体
        // 如果登录成功 {code:1,msg:"登录成功",data:{...用户信息...}}
        //     根据 data 生成 jwt, 并设置到响应头中
        // 如果登录失败 {code:0,msg:"登录失败",data:null}
        //    直接返回
        ServerHttpResponseDecorator decorator =
                new ServerHttpResponseDecorator(response) {

                    @Override
                    public Mono<Void> writeWith(Publisher<? extends DataBuffer> body) {
                        System.out.println("---------------1-------------");
                        Flux<DataBuffer> flux = Flux.from(body).map(dataBuffer -> {
                            // 读取响应体, 转换为字符串
                            byte[] bytes = new byte[dataBuffer.readableByteCount()];
                            dataBuffer.read(bytes);
                            String bodyStr = new String(bytes);
                            // 释放 dataBuffer
                            DataBufferUtils.release(dataBuffer);
                            try {
                                HashMap res = new ObjectMapper().readValue(bodyStr, HashMap.class);
                                System.out.println("res = " + res);
                                if(res.containsKey("data")){
                                    // 登录成功, 生成 jwt, 并设置到响应头中
                                    // 创建 jwt
                                    String jwt = JwtUtils.createJWT(30, (Map<String, Object>) res.get("data"));
                                    // 登录成功, 生成 jwt, 并设置到响应头中
                                    response.getHeaders().set("Authorization", "Bearer " + jwt);
                                }
                                // 将原始数据写回到响应体
                                response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
                                return response.bufferFactory().wrap(bytes);
                            } catch (JsonProcessingException e) {
                                throw new RuntimeException(e);
                            }
                        });
                        return super.writeWith(body);
                    }
                };
        return chain.filter(exchange.mutate().response(decorator).build());
    }

    private Mono<Void> myinfo(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 实现前置拦截
        // 从请求头中获取 jwt
        String jwt = exchange.getRequest().getHeaders().getFirst("Authorization");
        System.out.println("jwt = " + jwt);
        // 判断黑名单中是否包含该 jwt
        if(blackJwtSet.contains(jwt)){
            jwt = null;
        }
        Map myinfo = null;
        if (jwt != null && jwt.startsWith("Bearer ")) {
            // 校验 jwt
            try {
                myinfo = JwtUtils.parseJWT(jwt.substring(7));
            } catch (Exception e) {
                // 格式错误, 超时
                throw new RuntimeException("解析JWT令牌错误",e);
            }
        }
        Map result;
        if(myinfo == null){
            result = Map.of("code",0,"msg","未登录");
        } else {
            result = Map.of("code",1,"msg","已登录", "data", myinfo);
        }
        exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
        ObjectMapper mapper = new ObjectMapper();
        try {
            String json = mapper.writeValueAsString(result);
            // 创建返回结果
            DataBuffer body = exchange.getResponse().bufferFactory().wrap(json.getBytes());
            // 直接将结果返回到浏览器
            return exchange.getResponse().writeWith(Mono.just(body));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    private Set<String> blackJwtSet = Collections.synchronizedSet(new HashSet<>());

    private Mono<Void> logout(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 实现前置拦截
        // 提取 JWT 存入黑名单
        String jwt = exchange.getRequest().getHeaders().getFirst("Authorization");
        if(jwt!=null){
            blackJwtSet.add(jwt);
        }
        Map result = Map.of("code",1,"msg","退出成功");
        try {
            String json = new ObjectMapper().writeValueAsString(result);
            exchange.getResponse().getHeaders().setContentType(MediaType.APPLICATION_JSON);
            // 创建返回结果
            DataBuffer body = exchange.getResponse().bufferFactory().wrap(json.getBytes());
            // 直接将结果返回到浏览器
            return exchange.getResponse().writeWith(Mono.just(body));
        } catch (JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    @Data
    public static class Config {
        boolean enabled = false;
    }

    /**
     * 存在的问题:
     * 1. jwtToken 复制给他人使用, 如何阻止
     * 2. 基于 js sessionStoreage, 访问网页(点击超链接,无法写入Authorization), 如何实现权限的控制
     */

}
